package lib;

import java.awt.Point;
import java.util.HashMap;
import java.util.LinkedList;

import lib.grav.ForceHandler;
import lib.map.Map;
import lib.player.Player;
import lib.player.PlayerList;
import server.HighScore;
import server.TCPServer;
import server.TCPServerUnit;

/**
 * This class contains important data about one universe.
 * 
 * @author andreas
 * 
 */

/**
 * @commentator Simon Wang This method should be implemented here(it is
 *              initiated after every intervall in Blackhole.run())
 * 
 *              public static void updatePlayerInfos(String "Spielername",
 *              double xPos, double yPos, double xForce, yForce){ if
 *              (playerPositions[xPos][yPos] != null){ //if something is already
 *              at x,y Player2 = getPlayersName(xPos, yPos); //gets Playersname
 *              who's is already at x,y double OtherxForce =
 *              getOtherxForce(Player2); //gets the vectors from this player
 *              (playerPositions should contain his vectors) double OtheryForce
 *              = getOtheryForce(Player2);
 * 
 *              if (Player2 is human){ vectorUpdate(OtherxForce, OtheryForce);
 *              //send new position for Player who has sent the input
 *              Player2.vectorUpdate(xForce, yForce); //send new position for
 *              Player2 who was first at x,y (that means a collision from two
 *              players is calculated once)
 * 
 * 
 *              } if (Player2 is balken){ // if player2 was balken player1
 *              ("Spielername") waits until he leaves balken
 * 
 *              updateGameActions(); //wait for Player to move by himself (in
 *              "Keyboard-Input-class") updatePlayerInfos();
 * 
 *              } if (Player2 is rocket){ vectorUpdate(OtherxForce,
 *              OtheryForce); removePlayer(Rocket)
 * 
 *              } } else playerPositions[xPos][yPos] = {"Spielername", xForce,
 *              yForce}; //sets Player"Spielername" at this position because no
 *              one was there
 * 
 *              }
 */

public class Universe extends Thread {
	// public Point[] playerPositions; // contains current position of each
	// player.
	// private ForceHandler forceHandlers[];
	private boolean isHere[];
	private int maxPlayers = 32;
	private String name = "";

	public int gameNum;
	// public ArrayList<String> playersList;
	public HashMap<String, Integer> scores;
	public lib.map.Map map =null;
	public PlayerList pl;
	private int[] score = new int[2];

	private final Point[] Spwan = new Point[] {
			new Point(100, Config.RowCount * 10 - 100),
			new Point(Config.CollumnCount * 10 - 100, 100) };

	private final double PLAYER_MASS = 1000;
	private final int HEARTBEAT = 50; // time intervall in ms

	public static int Y = 0; // Y is the number of the last player existing
	private int playerCount = 0;
	public LinkedList<TCPServerUnit> unitsList = new LinkedList<TCPServerUnit>();

	/**
	 * Initializes playersList and creates an array with random positions for 32
	 * users.
	 * 
	 * @param gameNum
	 */
	public Universe(int gameNum, String mapName) {
		// System.out.println("parent" + u);
		scores = new HashMap<String, Integer>();
		this.gameNum = gameNum;
		this.map = new Map(this, mapName);
		pl = new PlayerList(map);

		isHere = new boolean[maxPlayers + 1];
		this.start();
		if (Config.debug)
			System.out.println("Universe Created");
	}

	public void setPlayerStartPosition(Player p) {
		isHere[p.nr] = true;
		int i = 0;
		while (!pl.move(p, (int) (Spwan[p.Team - 1].x + i++
				* Math.pow(-1, p.Team - 1)), (int) (Spwan[p.Team - 1].y + i++
				* Math.pow(-1, p.Team - 1)), this))
			;
		p.forceHandler = new ForceHandler(this, map.blackHole, p);
	}



	public String toString() {
		if (name.equals(""))
			return (char) (gameNum + 64) + "-verse";
		return name;
	}

	/**
	 * Sets the name of the universe.
	 * 
	 * @param name
	 */
	public void setUniverseName(String name) {
		this.name = name;
	}

	public String getUniverseName() {
		return name;
	}

	public boolean hasPlayer(String userName) {
		return pl.getPlayer(userName) != null && scores.containsKey(userName);
	}

	public void addPlayer(String userName, int team, TCPServerUnit u) {
		u.sendMap(map,true);
		Player p = new Player(0, 0, ++playerCount, team, userName);
		setPlayerStartPosition(p);
		p.unit = u;
		this.unitsList.add(u);
		pl.list.add(p);
		scores.put(userName, 0);
	}

	/** Removes a name from the playersList of the current universe. */
	public void removePlayer(String userName) {
		// add scores to overall highscore
		if (HighScore.highscore == null)
			HighScore.initHighScore();
		else {
			// find old entry of userName
			int oldScore = 0;
			for (int score : HighScore.highscore.values()) {
				if (HighScore.highscore.containsKey(userName)) {
					oldScore = score;
					break;
				}
			}
			if (!HighScore.highscore.containsKey(userName)) // new entry
				HighScore.highscore.put(userName, oldScore
						+ scores.get(userName));
			else { // update entry
				HighScore.highscore.remove(userName);
				HighScore.highscore.put(userName, oldScore
						+ scores.get(userName));
			}
		}
		// remove from local highscore
		broadcast(Commands.PRINT,"Player "+userName+" left the game.");
		pl.remove(pl.getPlayer(userName));
		scores.remove(userName);
	}

	public void run() {
		System.out.println("Heartbeat-Thread started." + playerCount);
		ForceHandler fh;
		while (true) {
			/**
			 * vectorUpdate(xForceAdd, yForceAdd); is initiated in other classes
			 * then BlackHole which cause forces (perhaps with notify())
			 */
			for (Player p : pl.list) {
				if (isHere[p.nr]) {
					fh = p.forceHandler;
					if (fh.inBlackHole
							&& !p.ITEM.win) {

					} else {
						fh.move();					
						if (pl.move(p, (int) fh.xPos, (int) fh.yPos, this)) {
							int stat = fh.work((int) PLAYER_MASS);

							if (stat == 1) {
								broadcast(Commands.PRINT, "Player: " + p.name
										+ " Won");
								setPoints(p, 1);
								setTeamPoints(p.Team, 1);
								map.createParallelUniverseMap();
								broadcast(map);
								for (Player _p : pl.list) {
									_p.ITEM.respwan(map, _p, this, pl,
											Config.CollumnCount);
									setPlayerStartPosition(_p);
								}

							}
							if (stat == 0) {
								broadcast(Commands.PRINT, "GAME OVER! "
										+ p.name + " lost!");

								if (p.points != 0) {
									setPoints(p, -1);
								}
								p.ITEM.respwan(map, p, this, pl,
										Config.CollumnCount);
								setPlayerStartPosition(p);
							}
							broadcast(Commands.POS, p.toString());
						} else {
							p.forceHandler = new ForceHandler(this, map.blackHole, p);
						}
						/**
						 * order is important: move at last
						 * 
						 */

					}

				}
			}
			try {
				sleep(HEARTBEAT);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public int getPlayerCount() {
		return playerCount;
	}

	public ForceHandler getForceHandler(int playerID) {
		return pl.getPlayer(playerID).forceHandler;
	}

	/**
	 * Sends a message to all TCPCleintUnits in unitsList.
	 * */
	public void broadcast(Commands cmd, String string) {
		TCPServer.log(getUniverseName(), cmd + " " + string);
		for (TCPServerUnit u : unitsList) {
			u.send(cmd, string, false);
		}
	}

	public void broadcast(Map m) {
		for (TCPServerUnit u : unitsList) {
			u.sendMap(m,true);
		}
	}

	private void broadcastPoints() {
		for (Player p : pl.list) {
			broadcast(Commands.POINTS, p.nr + " " + p.points);
		}
	}

	private void broadcastTeamPoints(int team) {
		broadcast(Commands.TEAMPOINTS, team + " " + score[team - 1]);

	}

	public void setTeamPoints(int team, int p) {
		score[team - 1] += p;
		broadcastTeamPoints(team);
	}

	public void setPoints(Player player, int i) {
		player.points += i;
		broadcastPoints();
	}

}
